﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Castle.Windsor;
using Microsoft.Practices.ServiceLocation;
using CommonServiceLocator.WindsorAdapter;
using Caramel.Web.Mvc.Metadata;
using MvcExample.Models;
using Castle.MicroKernel.Registration;
using MvcExample.Controllers;
using Caramel;
using Caramel.Castle;
using Caramel.Web.Mvc;
using Caramel.Web.Mvc.Castle;
using Caramel.Web.Mvc.Metadata.Conventions;

namespace MvcExample
{
	// Note: For instructions on enabling IIS6 or IIS7 classic mode, 
	// visit http://go.microsoft.com/?LinkId=9394801

	public class MvcApplication : HttpApplication
	{
		public static IWindsorContainer Container { get; set; }

		public static void RegisterGlobalFilters(GlobalFilterCollection filters)
		{
			filters.Add(new HandleErrorAttribute());
		}

		public static void RegisterRoutes(RouteCollection routes)
		{
			routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

			routes.
				MapRoute(
					"Default",
					"{controller}/{action}/{id}",
					new { controller = "Employee", action = "Index", id = UrlParameter.Optional }
				);

		}

		protected void Application_Start()
		{
			AreaRegistration.RegisterAllAreas();

			RegisterGlobalFilters(GlobalFilters.Filters);
			RegisterRoutes(RouteTable.Routes);

			ServiceLocator.SetLocatorProvider(() => this.BuildProvider());
			DependencyResolver.SetResolver(ServiceLocator.Current);
		}

		private IServiceLocator BuildProvider()
		{
			if (Container == null)
			{
				Container =
					new WindsorContainer().
						Register(
							Component.
								For<ModelValidatorProvider>().
								ImplementedBy<FluentModelValidatorProvider>(),

							Component.
								For<ModelMetadataProvider>().
								UsingFactoryMethod(
									() =>
									Fluently.
										ConfigureMetadata().
											Metadata(
												metadata =>
												metadata.
													AutoMetadata.
													Override<CreateEmployeeViewModel>(
														m =>
														{
															m.
																Apply(e => e.FirstName).
																DisplayName("Christian Name");

															m.
																Apply(e => e.LastName).
																DisplayName("Surname").
																Order(100);
														}
													).
													Conventions.
													AddFromAssemblyOf<IDPropertyConvention>().
													Conventions.
													AddFromAssemblyOf<PostalCodeDisplayNamePropertyConvention>()
											).
											BuildModelMetadataProvider()
								),
							AllTypes.
								FromAssemblyContaining<EmployeeController>().
								BasedOn<IController>().
								WithService.
								Self().
								Configure(r => r.LifestyleTransient())
						).
						Install(
							new LoggingInstaller(),
							new MvcInstaller()
						);
			}

			return new WindsorServiceLocator(Container);
		}
	}

	public class PersonDisplayNamePropertyConvention : IModelMetadataPropertyConvention
	{
		public void Apply(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName, PropertyModelMetadata instance)
		{
			instance.
				DisplayName(
					propertyName.
						Replace("FirstName", "ChristianName").
						Replace("LastName", "Surname").
						Replace("MiddleName", "Middle").
						ToSeparatedWords()
				);
		}

		public bool Accept(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
		{
			return
				propertyName.EndsWith("FirstName", StringComparison.InvariantCultureIgnoreCase) ||
				propertyName.EndsWith("MiddleName", StringComparison.InvariantCultureIgnoreCase) ||
				propertyName.EndsWith("LastName", StringComparison.InvariantCultureIgnoreCase);
		}
	}

	public class PostalCodeDisplayNamePropertyConvention : DisplayNamePropertyConvention
	{
		#region IConvention<PropertyMetadata> Members

		public override void Apply(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName, PropertyModelMetadata instance)
		{
			instance.
				DisplayName(propertyName.Replace("PostalCode", "ZipCode").ToSeparatedWords());
		}

		#endregion

		#region IConvention Members

		public override bool Accept(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
		{
			return propertyName.EndsWith("PostalCode", StringComparison.InvariantCultureIgnoreCase);
		}

		#endregion
	}
}